<template>
  <!-- 文件上传组件：可上传图片 视频等 -->
  <button type="button" @click="selectFile" class="btn btn-white btn-default btn-round">
    <i class="fa fa-upload"></i>
    {{text}}
  </button>
  <!-- 隐藏显示了上传控件 而是由button触发 -->
  <input ref="file" class="hidden" type="file" :id="inputId+'-input'" @change="uploadFile">
</template>

<script>
import axios from "axios";
import {ref} from "vue";

export default {
  name: 'file',
  components: {},
  props: {
    text: {
      type: String,
      default: '上传文件'
    },
    // 文件格式
    suffixs: {
      type: Array,
      default: () => []
    },
    // 上传成功回调
    afterUpload: {
      type: Function,
      default: null
    },
    // 文件上传控件id
    inputId: {
      type: String,
      default: 'file-upload-input'
    },
    // 文件分类 用途
    use: {
      type: String,
      default: ''
    },
    // 分片大小 默认20mb
    shardSize: {
      default: 20 * 1024 * 1024
    },
    // 上传位置：本地？oss? 视频点播 vod 灵活配置
    url: {
      type: String,
      default: 'oss-append'
    },

  },

  setup(props) {
    const file = ref();

    /**
     * 选择文件
     */
    const selectFile = () => {
      // 触发文件上传
      $('#' + props.inputId + '-input').trigger('click');
    }

    /**
     * 上传
     */
    const uploadFile = () => {
      let fileObj = file.value.files[0]; // 文件对象
      // 判断文件格式是否正确？jpg,jpeg,png
      let validateSuffix = false; // 效验标志
      let fileName= fileObj.name.toLowerCase(); // 文件名字+后缀
      let fileSuffix = fileName.split('.')[1]; // 文件后缀
      for (let i = 0; i < props.suffixs.length; i++) {
        if (props.suffixs[i] === fileSuffix) {
          validateSuffix = true;
          break;
        }
      }
      if (!validateSuffix) {
        Toast.warning("文件格式只能为:！" + props.suffixs.join(","));
        return;
      }

      // 文件分片传输
      let shardSize = props.shardSize; // 分片大小 20M
      let shardIndex = 1;// 分片索引 保存第几个分片了 其实第一个分片
      let size = fileObj.size; // 文件大小
      let shardTotal = Math.ceil(size / shardSize); // 分片总数

      // 生成文件标识
      let str = fileObj.name + fileObj.size + fileObj.lastModified; // 文件信息字符串
      let key = hex_md5(str);// 文件唯一key
      let key10 = parseInt(key, 16); // 16 转 10进制
      let key62 = Tool._10to62(key10); // 10 转 62进制 目的是key变短

      let param = {
        name: fileName,
        suffix: fileSuffix,
        use: props.use,
        shardIndex: shardIndex,
        shardSize: shardSize,
        shardTotal: shardTotal,
        size: size,
        key: key62,
      }

      // 检查分片状态
      check(param);
    }

    /**
     * 文件转base64 上传
     */
    const upload = (param) => {
      let shardIndex = param.shardIndex;
      let shardTotal = param.shardTotal;
      let shardSize = param.shardSize;
      let fileShard = getFileShard(shardIndex, shardSize);

      // 将图片转为Base64传输 base64格式：data:application/octet-stream;base64,+r2c...
      let fileReader = new FileReader();
      Progress.show(parseInt((shardIndex - 1) * 100 / shardTotal)); // 进度条
      fileReader.onload = (e) => { // 事件监听
        let base64 = e.target.result; // 转为base64
        param.shard = base64;

        // 注：本地服务上传接口调用/admin/upload
        // 注：oss服务上传接口调用/admin/oss-append
        axios.post(process.env.VUE_APP_SERVER + '/file/admin/' + props.url, param).then((response) => {
          const resp = response.data;
          Loading.hide();
          if (resp.success) {
            let f = resp.content;
            Progress.show(parseInt(shardIndex * 100 / shardTotal));
            if (shardIndex < shardTotal) {
              param.shardIndex = param.shardIndex + 1; // 索引+1
              upload(param); // 递归调用
            } else {
              Toast.success("文件上传成功！");
              Progress.hide();
              props.afterUpload(f); // 给父组件返回文件 对象信息
              file.value.value = null; // 这行代码用来清空input的值，以便下次选择文件
            }
          }
        })
      }
      fileReader.readAsDataURL(fileShard);
    }

    /**
     * 文件状态：检查分片是否存在 上传到哪了？
     */
    const check = (param) => {
      console.log("开始检查分片状态");
      axios.get(process.env.VUE_APP_SERVER + "/file/admin/check/" + param.key).then((response) => {
        const resp = response.data;
        if (resp.success) {
          let obj = resp.content;
          if (!obj) {
            console.log("未找到上传记录，从1片开始上传");
            upload(param);
          } else {
            if (obj.shardIndex < param.shardTotal) {
              param.shardIndex = obj.shardIndex + 1;
              console.log("文件上传从第" + param.shardIndex + "片开始上传");
              upload(param);
            } else if (obj.shardIndex === param.shardTotal) {
              Toast.success("极速秒传成功！");
              props.afterUpload(obj);
            }
          }
        } else {
          Toast.error("文件上传失败");
        }
      })
    }

    /**
     * 获取下一次分片
     */
    const getFileShard = (shardIndex, shardSize)=> {
      let fileObj = file.value.files[0]; // 文件对象
      let start = (shardIndex - 1) * shardSize;	//当前分片起始位置
      let end = Math.min(fileObj.size, start + shardSize); // 分片结束位置
      let fileShard = fileObj.slice(start, end); // 分片对象 blob 创建一个新的Blob对象

      return fileShard;
    }

    return {
      selectFile,
      uploadFile,
      file,
    }
  }
}
</script>

<style scoped>
</style>
